/*
css transform-origin 对应
Keyword 	Value
left 	0%
center 	50%
right 	100%
top 	0%
bottom 	100%
*/

/*
export enum HorizontalOrigin {
     * The origin is at the horizontal center of the object.
    CENTER = 0,
     * The origin is on the left side of the object.
    LEFT = 1,
     * The origin is on the right side of the object.
    RIGHT = -1
}
*/

/*
export enum VerticalOrigin {
     * The origin is at the vertical center between <code>BASELINE</code> and <code>TOP</code>.
    CENTER = 0,
     * The origin is at the bottom of the object.
    BOTTOM = 1,
     * If the object contains text, the origin is at the baseline of the text, else the origin is at the bottom of the object.
    BASELINE = 2,
     * The origin is at the top of the object.
    TOP = -1
}
*/

import * as Cesium from 'cesium'
// https://cesium.com/learn/cesiumjs/ref-doc/BillboardGraphics.html?classFilter=billboard
export class DomEntityManual {
  constructor(
    {
      id,
      position,
      html,
      horizontalOrigin = Cesium.HorizontalOrigin.LEFT,
      verticalOrigin = Cesium.VerticalOrigin.TOP,
      pixelOffset = new Cesium.Cartesian2(0, 0),
      distanceDisplayCondition = new Cesium.DistanceDisplayCondition(0.1, 100_000),
      scaleByDistance = new Cesium.NearFarScalar(50, 1.0, 150, 0.4),
      // TODO: 待实现
      // 从上到下优先级递减
      // sizeInMeters,
      // pixelOffsetScaleByDistance ,
      show = true,
    },

    viewer = window.cesiumViewer,
  ) {
    if (!Cesium.defined(html)) {
      throw new Error('未传参html')
    }

    if (!Cesium.defined(position)) {
      throw new Error('未传参 position')
    }
    this._id = id
    this._position = position
    this._html = html
    this._verticalOrigin = verticalOrigin
    this._horizontalOrigin = horizontalOrigin
    this._pixelOffset = pixelOffset
    this._distanceDisplayCondition = distanceDisplayCondition
    this._scaleByDistance = scaleByDistance
    this._show = show
    this.viewer = viewer
    this.rootDom = null
    this.isDestroyed = false
    this.init()
    this.update()
  }

  init() {
    const rootDom = (this.rootDom = document.createElement('div'))

    rootDom.className = 'dom-entity outer'

    Object.assign(rootDom.style, {
      position: 'fixed',
      pointerEvents: 'none',
      visibility: 'hidden',
      // (1-x)*50% , (1+y)*50%
      transformOrigin: `${(1 - this._horizontalOrigin) * 50}% ${(1 + this._verticalOrigin) * 50}%`,
    })

    if (typeof this._html === 'string') {
      rootDom.innerHTML = this._html
    } else {
      rootDom.appendChild(this._html)
    }

    document.body.appendChild(rootDom)
  }
  update() {
    const scratchCartesian2 = new Cesium.Cartesian2()
    this.viewer.scene.cartesianToCanvasCoordinates(this._position, scratchCartesian2)

    if (Cesium.defined(scratchCartesian2)) {
      this.rootDom.style.left = `${scratchCartesian2.x + this._pixelOffset.x}px`

      this.rootDom.style.top = `${scratchCartesian2.y + this._pixelOffset.y}px`

      if (this._show) {
        const distance = Cesium.Cartesian3.distance(
          this.viewer.scene.camera.position,
          this._position,
        )

        const { near, far } = this._distanceDisplayCondition

        if (near < distance && distance < far) {
          if (this.judgeOcclusionByGlobe(this._position, this.viewer.scene)) {
            this.rootDom.style.visibility = 'hidden'
          } else {
            const { near, nearValue, far, farValue } = this._scaleByDistance

            let scale = 1.0
            if (distance < near) {
              scale = nearValue
            } else if (distance > far) {
              scale = farValue
            } else {
              scale = Cesium.Math.lerp(nearValue, farValue, (distance - near) / (far - near))
            }

            this.rootDom.style.transform = `translate(${(this._horizontalOrigin - 1) * 50}%,${(-this._verticalOrigin - 1) * 50}%) scale(${scale})`

            this.rootDom.style.visibility = 'visible'
          }
        } else {
          this.rootDom.style.visibility = 'hidden'
        }
      } else {
        this.rootDom.style.visibility = 'hidden'
      }
    }
  }

  // 判断是否被地球遮挡

  judgeOcclusionByGlobe(position, scene) {
    const occluder = new Cesium.EllipsoidalOccluder(Cesium.Ellipsoid.WGS84, scene.camera.position)
    return !occluder.isPointVisible(position)
  }

  destroy() {
    if (this.isDestroyed) return
    document.body.removeChild(this.rootDom)
    this.isDestroyed = true
  }
  get show() {
    return this._show
  }
  set show(flag) {
    this._show = flag
    this.rootDom.style.visibility = flag ? 'visible' : 'hidden'
  }
}
